#include "my_header.h"
#include "threadPool.h"
#include "loop_queue.h"
#include"handleEpollEvent.h"
int exitPipe[2];//handler只能访问全局的数据
LoopQueue_t *time_queue; // 时间轮盘对象
                         //
void handler(int signum){
    printf("Parent got signal, signum = %d\n", signum);
    write(exitPipe[1],"1",1);
}
int main(int argc, char *argv[])
{
    // ./server 192.168.72.128 12345 3
    ARGS_CHECK(argc,4);
    pipe(exitPipe);
    if(fork()){
        close(exitPipe[0]); // 父进程关闭管道的读端
        signal(SIGUSR1,handler);
        wait(NULL);
        printf("Parent is going to exit!\n");
        exit(0);
    }
    close(exitPipe[1]);

        // 初始化时间轮盘
    time_queue = LoopQueueEpollInit();
    
    threadPool_t threadPool;
    //初始化数据结构
    int workerNumber = atoi(argv[3]);
    threadPoolInit(&threadPool, workerNumber);
    //创建若干个子线程，修改tidArr的内容
    makeWorker(&threadPool);

    //创建客户端队列并初始化，以哈希表的形式存在
    HashMap* hashmap = hashmap_create();

    //

    //建立tcp连接，得到sockfd
    int sockfd = tcpInit(argv[1],argv[2]);
    //用epoll进行监听
    int epfd = epoll_create(1);
    epollAdd(epfd,sockfd);
    epollAdd(epfd,exitPipe[0]);

    //连接数据库
    MYSQL *mysql = initDatabaseConnect();
    struct epoll_event readyset[1024];
    // 使用alarm定时调用timecheck函数
    alarm(30); // 每隔30秒调用一次timecheck
    while(1){
        //每0.1s醒来一次，用于超时踢人，先写为-1，踢人功能加入后改为100
        int readyNum = epoll_wait(epfd,readyset,1024,-1);
        DEBUG_PRINT("readyNum = %d\n",readyNum);
        for(int i = 0; i < readyNum; ++i){
            if(readyset[i].data.fd == sockfd){
                //sockfd就绪，说明有新的客户端的连接请求
                int netfd = accept(sockfd,NULL,NULL);
                //将netfd加入epoll监听
                epollAdd(epfd,netfd);
                // 将新连接添加到时间轮盘
                handleNewClient(time_queue, netfd);
            }else if(readyset[i].data.fd == exitPipe[0]){
                //监听到父进程和子进程通信的管道信号，即退出信号
                pthread_mutex_lock(&threadPool.mutex);
                threadPool.exitFlag = 1;//将退出标志位改为1
                pthread_cond_broadcast(&threadPool.cond);//唤醒所有子线程
                pthread_mutex_unlock(&threadPool.mutex);
                for(int j = 0; j < workerNumber; ++j){
                    //等待所有子线程结束
                    pthread_join(threadPool.tidArr.arr[j],NULL);
                }
                //结束子进程
                exit(0);
            }else{
                //监听到客户端的消息
                int netfd = readyset[i].data.fd;
                updateNetfdPosition(time_queue, netfd);
                DEBUG_PRINT("netfd = %d\n",netfd);
                //收取小火车的信息
                train_t train;
                bzero(&train,sizeof(train));
                recvn(netfd,&train.type,sizeof(train.type));
                DEBUG_PRINT("train.type = %d\n",train.type);
                recvn(netfd,&train.length,sizeof(train.length));
                recvn(netfd,train.data,train.length);
                DEBUG_PRINT("train.data = %s\n",train.data);
                //字符串切割
                userInfo_t userInfo;
                user_t user;
                bzero(&userInfo,sizeof(userInfo));
                bzero(&user,sizeof(user));
                strToken(train.data,&userInfo);
                DEBUG_PRINT("strToken sucess\n");
                if(train.type==REGISTER){
                    //用户注册
                    //注册
                    char *salt = createRandomSalt();
                    memcpy(user.salt,salt,strlen(salt));
                    DEBUG_PRINT("salt = %s\n",salt);
                    send(netfd,salt,strlen(salt),MSG_NOSIGNAL);
                    DEBUG_PRINT("send salt over\n");
                    //接收密文
                    bzero(&train,sizeof(train));
                    recvn(netfd,&train.type,sizeof(train.type));
                    recvn(netfd,&train.length,sizeof(train.length));
                    recvn(netfd,train.data,train.length);
                    memcpy(userInfo.ciphertext,train.data,train.length);
                    DEBUG_PRINT("recv cipherText = %s\n",userInfo.ciphertext); 

                    //进行注册
                    int ret = registerUser(mysql,&userInfo,&user);
                    send(netfd,&ret,sizeof(ret),MSG_NOSIGNAL);
                    DEBUG_PRINT("after registerUser,ret = %d",ret);
                }else if(train.type == LOGIN){
                    //用户登录
                    //接收用户名
                    //bzero(&train,sizeof(train));
                    //recvn(netfd,&train.type,sizeof(train.type));
                    //recvn(netfd,&train.length,sizeof(train.length));
                    //recvn(netfd,train.data,train.length);
                    //DEBUG_PRINT("train.data = %s",train.data);
                    //memcpy(userInfo.userName,train.data,train.length);
                    //DEBUG_PRINT("userInfo.userName = %s\n",userInfo.userName);

                    //发盐值
                    userSalt(mysql,&userInfo,&user);
                    DEBUG_PRINT("user.salt = %s\n",user.salt);
                    send(netfd,user.salt,strlen(user.salt),MSG_NOSIGNAL);
                    DEBUG_PRINT("user.salt = %s\n",user.salt);

                    //接收密文
                    bzero(&train,sizeof(train));
                    recvn(netfd,&train.type,sizeof(train.type));
                    recvn(netfd,&train.length,sizeof(train.length));
                    recvn(netfd,train.data,train.length);
                    DEBUG_PRINT("after recvn ciphertext = %s\n",train.data);
                    memcpy(userInfo.ciphertext,train.data,train.length);
                    DEBUG_PRINT("userInfo.ciphertext = %s\n",userInfo.ciphertext);

                    //进行登录
                    int ret = userIsExit(mysql,&userInfo,&user);
                    DEBUG_PRINT("after userIsExit ,ret = %d\n",ret);

                    //将返回值发给客户端，成功是0，失败是1
                    send(netfd,&ret,sizeof(ret),MSG_NOSIGNAL);
                    DEBUG_PRINT("login success\n");
                    
                    //发送token给到客户端
                    char *token = generate_token(userInfo.userName,userInfo.userPassword);
                    DEBUG_PRINT("token = %s\n",token);
                    send(netfd,token,strlen(token),MSG_NOSIGNAL);
                    DEBUG_PRINT("send token success\n");
                    Canalysis_t* canalysis = (Canalysis_t*)calloc(1,sizeof(Canalysis_t));
                    //这边赋值mysql
                    canalysis->mysql = mysql;
                    /* canalysis->mysql = mysql; */
                    sprintf(canalysis->username,"%s",userInfo.userName);
                    initCanalysis(canalysis);
                    hashmap_put(hashmap,userInfo.userName,canalysis);

                }else{
                    //其他命令
                    //需要先进性token验证
                    char *token = generate_token(userInfo.userName,userInfo.userPassword);
                    if(strcmp(token,userInfo.ciphertext)!=0){
                        //token不对
                        char buf[]="token is wrong";
                        sendMsg(netfd,buf);
                        continue;
                    }

                    Canalysis_t* userstatus;
                    userstatus = hashmap_get(hashmap,userInfo.userName);

                    int sret = separationCommand(userInfo.commamd,userstatus);
                    sret=userstatus->flag;
                    if(sret==-1){
                        DEBUG_PRINT("commamd is woring\n");
                        //返回结果都在userstatus->result;可以直接发送 

                    }
                    if(sret>=1&&sret<=5){
                        //发字符串
                        LOG("The client request directory information");
                        DEBUG_PRINT("worker recv a request for directory information\n");
                        //返回结果都在userstatus->result;可以直接发送 
                        sendMsg(readyset[i].data.fd,userstatus->result);
                    }
                    else if(sret==6||sret==7){
                        LOG("The client request download a file");
                        DEBUG_PRINT("worker recv a request for dowload a file\n");
                        //发文件
                        DEBUG_PRINT("current path: %s\n", getcwd(NULL, 0));
                        //将netfd入队，交给子线程来做
                        pthread_mutex_lock(&threadPool.mutex);
                        threadPool.command = userstatus;
                        enQueue(&threadPool.taskQueue,readyset[i].data.fd);
                        pthread_cond_broadcast(&threadPool.cond);
                        pthread_mutex_unlock(&threadPool.mutex);
                    }
                    else if(sret==8){
                        LOG("The client request remove a file");
                        DEBUG_PRINT("The client request remove a file\n");
                        //返回结果都在userstatus->result;可以直接发送 
                        sendMsg(readyset[i].data.fd,userstatus->result);
                    }       
                }
                // 更新连接在时间轮盘上的位置
                updateNetfdPosition(time_queue, netfd);
            }
        }
    }
    return 0;
}
